package com.gitee.wsl.eventbus

import com.gitee.wsl.eventbus.event4k.EventHandler
import com.gitee.wsl.eventbus.event4k.Handler
import com.gitee.wsl.reflect.TypeInfo
import com.gitee.wsl.reflect.typeInfo
import com.gitee.wsl.struct.register.RegisterHook
import kotlinx.coroutines.flow.Flow

interface EventBus<K> {

    suspend fun <Event> publish(key: K, event: Event): Map<RegisterHook<*>, *>

    suspend fun <Event, OutPut> register(key: K, handler: EventHandler<Event, OutPut>): RegisterHook<OutPut>

    suspend fun clear()

    fun isExits(key: K): Boolean

    fun <Event> asFlow(key: K): Flow<Event>

    fun caseKey(type:TypeInfo):K

    suspend fun <Event, OutPut> registerAndIgnoreErrors(
        key: K,
        handler: Handler<Event, OutPut>
    ): RegisterHook<OutPut>

    suspend fun <Event, OutPut> consumeOnce(key: K, handler: EventHandler<Event, OutPut>): OutPut

    suspend fun <Event, OutPut> consumeOnceAndIgnoreErrors(key: K, handler: Handler<Event, OutPut>): OutPut

    companion object
}

suspend inline fun <reified Event,K> EventBus<K>.publish(event: Event) =
    this.publish(caseKey(typeInfo<Event>()), event)

suspend inline fun <reified Event, OutPut,K> EventBus<K>.register(handler: EventHandler<Event, OutPut>): RegisterHook<OutPut> =
    this.register(caseKey(typeInfo<Event>()), handler)

suspend inline operator fun <reified Event,K> EventBus<K>.plus(event: Event) = publish(event)

suspend inline fun <reified Event, OutPut,K> EventBus<K>.registerAndIgnoreErrors(noinline handler: Handler<Event, OutPut>): RegisterHook<OutPut> =
    this.registerAndIgnoreErrors(caseKey(typeInfo<Event>()),handler)

/**
 * consume a single event and deregister itself, pretty useless,
 * there is no guarantee you receive the last event happening while this is being registered
 */
suspend inline fun <reified Event, OutPut,K> EventBus<K>.consumeOnce(handler: EventHandler<Event, OutPut>): OutPut =
    this.consumeOnce(caseKey(typeInfo<Event>()), handler)

suspend inline fun <reified Event, OutPut,K> EventBus<K>.consumeOnceAndIgnoreErrors(noinline handler: Handler<Event, OutPut>): OutPut =
    this.consumeOnceAndIgnoreErrors(caseKey(typeInfo<Event>()), handler)
